﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Gui15Puzzle
{
    public partial class Form1 : Form
    {
        #region member data

        private int[,] puzzle = new int[4, 4];
        private int x, y;
        private int moves;
        //Bitmap btmp = Bitmap.FromFile("bild_resize.jpg");
        private Image img = Image.FromFile("bild_resize.jpg");
        private Bitmap bimg;

        #endregion

        #region private functions

        private void Swap(ref int x, ref int y)
        {
            int temp = x;
            x = y;
            y = temp;
        }

        private void Reset()
        {
            moves = 0;
            bimg = new Bitmap(img, new Size(356, 280));
            #region Create solvable board with random order

            bool puzzleIsSolvable = false;

            //create array of numbers
            int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 15};

            while (!puzzleIsSolvable)
            {
                //randomize the numbers array
                Random r = new Random();
                for (int i = 0; i < 16; i++)
                {
                    Swap(ref numbers[i], ref numbers[r.Next() % 16]);
                }

                //check if solvable
                int sum = 0;
                for (int i = 0; i < 16; i++)
                {
                    if (numbers[i] > 0)
                    {
                        for (int j = i + 1; j < 16; j++)
                        {
                            if (numbers[j] < numbers[i])
                            {
                                sum += numbers[j];
                            }
                        }
                    }
                    else    //if starting position
                    {
                        switch (i)
                        {
                            case 0:
                                sum += 6;
                                break;
                            case 1:
                                sum += 5;
                                break;
                            case 2:
                                sum += 4;
                                break;
                            case 3:
                                sum += 3;
                                break;
                            case 4:
                                sum += 5;
                                break;
                            case 5:
                                sum += 4;
                                break;
                            case 6:
                                sum += 3;
                                break;
                            case 7:
                                sum += 2;
                                break;
                            case 8:
                                sum += 4;
                                break;
                            case 9:
                                sum += 3;
                                break;
                            case 10:
                                sum += 2;
                                break;
                            case 11:
                                sum++;
                                break;
                            case 12:
                                sum += 3;
                                break;
                            case 13:
                                sum += 2;
                                break;
                            case 14:
                                sum++;
                                break;
                            default:
                                break;
                        }
                    }
                }

                if (sum % 2 == 1)
                {
                    puzzleIsSolvable = true;
                }

                //set values on puzzle board from numbers array
                int k = 0;
                for (int i = 0; i < 4; i++)
                {
                    for (int j = 0; j < 4; j++)
                    {
                        puzzle[i, j] = numbers[k];
                        if (numbers[k] == 0)
                        {
                            x = j;
                            y = i;
                        }
                        k++;
                    }
                }
            }

            #endregion
        }

        private bool CheckWin()
        {
            int k = 1;
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    if (puzzle[i, j] != k % 16)
                    {
                        return false;
                    }
                    k++;
                }
            }

            MessageBox.Show("Congratulations!\nYou solved the puzzle in " + moves + " moves!", "Congratulations!");
            Reset();
            this.Invalidate();
            return true;
        }

        #endregion

        #region constructors/destructors

        public Form1()
        {
            InitializeComponent();
            Reset();                    
        }
        
        #endregion

        #region events

        private void quitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void resetToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Reset();
            this.Invalidate();
        }

        private void Form1_MouseClick(object sender, MouseEventArgs e)
        {
            //Point p = MousePosition;
            int mouseX = e.X / 89;
            int mouseY = (e.Y - 25) / 70;
            //LabelTurns.Text = (mouseX.ToString() + ", " + mouseY.ToString());
            //LabelTest.Text = (this.x + ", " + this.y);
            //LabelTurns.Text = (this.x.ToString() + ", " + this.y.ToString());

            if (mouseX == this.x)
            {
                if (mouseY < this.y)
                {
                    //security check
                    if (mouseY < 0)
                    {
                        mouseY = 0;
                    }//

                    for (int i = this.y; i > mouseY; i--)
                    {
                        Swap(ref puzzle[i, mouseX], ref puzzle[i - 1, mouseX]);
                        moves++;
                        this.Invalidate();  
                        CheckWin();
                    }
                }
                else if (mouseY > this.y)
                {
                    //security check
                    if (mouseY > 3)
                    {
                        mouseY = 3;
                    }//

                    for (int i = this.y; i < mouseY; i++)
                    {
                        Swap(ref puzzle[i, mouseX], ref puzzle[i + 1, mouseX]);
                        moves++;
                        this.Invalidate();  
                        CheckWin();
                    }
                }
            }
            else if (mouseY == this.y)
            {
                if (mouseX < this.x)
                {
                    //security check
                    if (mouseX < 0)
                    {
                        mouseX = 0;
                    }//

                    for (int i = this.x; i > mouseX; i--)
                    {
                        Swap(ref puzzle[mouseY, i], ref puzzle[mouseY, i - 1]);
                        moves++;
                        this.Invalidate();  
                        CheckWin();
                    }
                }
                else if (mouseX > this.x)
                {
                    //security check
                    if (mouseX > 3)
                    {
                        mouseX = 3;
                    }//

                    for (int i = this.x; i < mouseX; i++)
                    {
                        Swap(ref puzzle[mouseY, i], ref puzzle[mouseY, i + 1]);
                        moves++;
                        this.Invalidate();  
                        CheckWin();
                    }
                }
            }

            
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Up:
                    if (y > 0)
                    {
                        Swap(ref puzzle[y, x], ref puzzle[y - 1, x]);
                        y--;
                        moves++;
                        this.Invalidate();
                        CheckWin();
                    }
                    break;
                case Keys.Down:
                    if (y < 3)
                    {
                        Swap(ref puzzle[y, x], ref puzzle[y + 1, x]);
                        y++;
                        moves++;
                        this.Invalidate();
                        CheckWin();
                    }
                    break;
                case Keys.Left:
                    if (x > 0)
                    {
                        Swap(ref puzzle[y, x], ref puzzle[y, x - 1]);
                        x--;
                        moves++;
                        this.Invalidate();
                        CheckWin();
                    }
                    break;
                case Keys.Right:
                    if (x < 3)
                    {
                        Swap(ref puzzle[y, x], ref puzzle[y, x + 1]);
                        x++;
                        moves++;
                        this.Invalidate();
                        CheckWin();
                    }
                    break;
                default:
                    break;
            }
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = CreateGraphics();
            Pen penBlack = new Pen(Brushes.Black);
            g.DrawRectangle(penBlack, new Rectangle(new Point(0, 25), new Size(356, 280)));

            //size of each tile: 89x64
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    g.DrawRectangle(penBlack, new Rectangle(new Point(i * 89, 25 + j * 70), new Size(89, 70)));
                    if (puzzle[j, i] != 0)
                    {
                        //g.FillRectangle(Brushes.White, new Rectangle(new Point(1 + i * 89, 26 + j * 70), new Size(88, 69)));
                        //(puzzle[j,i] - 1) % 4 = xcord
                        //(puzzle[j,i] - 1) / 4 = ycord
                        g.DrawImage(bimg, new Rectangle(new Point(i * 89, 25 + j * 70), new Size(89, 70)), ((puzzle[j, i] - 1) % 4) * 89, ((puzzle[j, i] - 1) / 4) * 70, 89, 70, GraphicsUnit.Pixel);
                        g.DrawString(puzzle[j, i].ToString(), new Font(FontFamily.GenericSansSerif, 8, FontStyle.Bold), Brushes.White, (1 + i * 89) + 10, (26 + j * 70) + 50);
                    }
                    else
                    {
                        g.FillRectangle(Brushes.DarkGray, new Rectangle(new Point(1 + i * 89, 26 + j * 70), new Size(88, 69)));
                        x = i;
                        y = j;
                        //LabelTurns.Text = (x.ToString() + ", " + y.ToString());
                    }
                }
            }

            LabelTurns.Text = "Moves: " + moves.ToString();
        }

        private void chooseImageToolStripMenuItem_Click(object sender, EventArgs e)
        {            
            switch (ChooseImageDialog.ShowDialog())
            {
                case DialogResult.OK:
                    img = Image.FromFile(ChooseImageDialog.FileName);
                    Reset();
                    this.Invalidate();
                    break;
                default:
                    break;
            }
            
        }

        #endregion
    }
}
